Skip to content

fix(tts): don't mark FallbackAdapter primary unavailable on abort-before-first-audio#1290

Open
mrniket wants to merge 2 commits intolivekit:mainfrom
lottiehq-oss:fix/tts-fallback-abort-no-mark-unavailable
Open

fix(tts): don't mark FallbackAdapter primary unavailable on abort-before-first-audio#1290
mrniket wants to merge 2 commits intolivekit:mainfrom
lottiehq-oss:fix/tts-fallback-abort-no-mark-unavailable

Conversation

@mrniket
Copy link
Copy Markdown
Contributor

@mrniket mrniket commented Apr 22, 2026

Description

tts.FallbackAdapter marks the primary provider unavailable whenever a SynthesizeStream is aborted before its first audio frame arrives. Caller interruptions within the provider's typical TTFA window (a few hundred ms) routinely trip this, forcing subsequent utterances onto the fallback TTS for the entire recoveryDelayMs — silent dead air during that window if the fallback is misbehaving.

Root cause: when the outer abort fires before any audio is received, FallbackSynthesizeStream.run() drops through to the !sawRawAudio guard and throws APIConnectionError("TTS stream completed but no audio was received"). The outer catch cannot distinguish that from a real silent provider failure and calls markUnAvailable(i).

Changes Made

  • agents/src/tts/fallback_adapter.ts — Short-circuit the !sawRawAudio branch in FallbackSynthesizeStream.run() when abortController.signal.aborted. On abort, emit END_OF_STREAM and return cleanly instead of throwing. Real silent provider failures (where abort is not signalled) still throw and still mark the primary unavailable.

  • agents/src/tts/fallback_adapter.test.ts — Added a regression test. The primary emits no audio; stream.close() is called before the first frame; the test asserts adapter.status[0].available === true. Without the fix the primary is marked unavailable. The two existing "silent failure triggers fallback" tests still pass, guarding the regression in the other direction.

Testing

  • pnpm vitest run agents/src — 684 passed, 2 skipped.
  • pnpm format:check clean.
  • Added test fails on main with expected false to be true and passes with the fix.

…ore-first-audio

A caller interruption within the window between text push and first audio frame
was hitting the !sawRawAudio guard in FallbackSynthesizeStream.run and being
re-raised as APIConnectionError, which the outer catch translated into
markUnAvailable(primary) — forcing subsequent utterances onto the fallback TTS
for the recovery window.

Treat abort-before-first-audio as a clean interruption: emit END_OF_STREAM and
return without throwing. Real silent provider failures are unaffected.
@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented Apr 22, 2026

⚠️ No Changeset found

Latest commit: fe920d6

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

…abort

- Match the success-path cleanup: await readInputLLMStream on the abort
  branch too, avoiding a potential dangling promise if input later throws.
- Trim verbose test comments and the new source comment.
@mrniket mrniket marked this pull request as ready for review April 22, 2026 13:45
Copy link
Copy Markdown
Contributor

@devin-ai-integration devin-ai-integration Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Devin Review: No Issues Found

Devin Review analyzed this PR and found no potential bugs to report.

View in Devin Review to see 2 additional findings.

Open in Devin Review

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant